﻿using IdentityManagement;
using Microsoft.AspNetCore.Identity;
using PermissionManagement;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Data;
using Volo.Abp.Security.Claims;
using Xms.Abp.Application.Services;
using Xms.Abp.Domain.Repositories;
using IdentityUser = IdentityManagement.IdentityUser;

namespace AccountManagement;

public class AccountService : XmsApplicationService<IdentityUser, Guid>, IAccountService
{
    protected SignInManager<IdentityUser> SignInManager => LazyInject<SignInManager<IdentityUser>>();
    protected IOrganizationUnitRepository OrganizationUnitRepository => LazyInject<IOrganizationUnitRepository>();
    protected IIdentityRoleRepository RoleRepository => LazyInject<IIdentityRoleRepository>();
    protected IXmsRepository<PermissionGrant, Guid> PermissionGrantRepository
        => LazyInject<IXmsRepository<PermissionGrant, Guid>>();

    protected IIdentityUserRepository UserRepository { get; }

    public AccountService(IIdentityUserRepository repository) : base(repository)
    {
        UserRepository = repository;
    }

    public async Task<ProfileResult> GetProfileAsync()
    {
        if (CurrentUser.IsAuthenticated)
        {
            var user = await UserRepository.GetAsync(CurrentUser.Id.Value, true);
            var organization = user.OrganizationUnits != null && user.OrganizationUnits.Any() ? await OrganizationUnitRepository.GetAsync(user.OrganizationUnits.First().OrganizationUnitId) : null;
            var result = new ProfileResult
            {
                UserName = user.UserName,
                Avatar = user.Avatar.IsNullOrWhiteSpace() ? "/avatar2.png" : user.Avatar,
                NickName = user.ExtraProperties.ContainsKey(nameof(ProfileResult.NickName)) ? user.GetProperty<string>(nameof(ProfileResult.NickName)) : user.Name,
                IsEnabled = user.IsActive,
                IsLogin = CurrentUser.IsAuthenticated,
                Name = user.Name,
                Organization = organization?.DisplayName,
                PhoneNumber = user.PhoneNumber,
                IsHost = !CurrentUser.TenantId.HasValue,
            };
            var roleIds = user.Roles?.Select(r => r.RoleId);
            if (roleIds != null && roleIds.Any())
            {
                var role = await (await RoleRepository.AsNoTrackingAsync()).Where(r => roleIds.Contains(r.Id)).Select(r => r.Name).ToArrayAsync();
                result.Roles = role;
                var rids = roleIds.Select(r => r.ToString());
                var list = await (await PermissionGrantRepository.AsNoTrackingAsync()).Where(r => r.ProviderName == "R" && rids.Contains(r.ProviderKey)).Select(r => new { r.ModuleName, r.FunctionName }).Distinct().ToListAsync();
                result.Permissions = list.GroupBy(r => r.ModuleName).Select(r => new IdentityRolePermissionResult
                {
                    ModuleName = r.Key,
                    Items = r.Select(t => t.FunctionName).ToArray(),
                }).ToList();
            }
            return result;
        }
        else
        {
            return new ProfileResult
            {
                Avatar = "/avatar.png",
                NickName = "未登录",
                IsEnabled = false,
                IsLogin = false,
                Name = "未登录",
            };
        }
    }

    public async Task ChangePwdAsync(ChangePwdParameter parameter)
    {
        var user = await UserRepository.GetAsync(CurrentUser.Id.Value);
        var result = await SignInManager.UserManager.ChangePasswordAsync(user, parameter.OldPassword, parameter.NewPassword);
        if (!result.Succeeded)
        {
            var error = result.Errors?.First();
            throw new UserFriendlyException(error?.Description ?? "密码修改失败", error?.Code);
        }
    }

    public async Task UpdateProfileAsync(UpdateProfileParameter parameter)
    {
        var user = await UserRepository.GetAsync(CurrentUser.Id.Value);
        user.Avatar = parameter.Avatar;
        user.SetPhoneNumber(parameter.PhoneNumber, false);
        user.Name = parameter.Name;
        user.NickName = parameter.NickName;
        await UserRepository.UpdateAsync(user);
    }

    public async Task<bool> LoginAsync(LoginParameter parameter)
    {
        var user = await UserRepository.FindForLoginAsync(parameter.Username);

        if (user == null)
        {
            throw new UserFriendlyException("账号不存在");
        }
        var result = await SignInManager.CheckPasswordSignInAsync(user, parameter.Password, true);
        if (result.Succeeded)
        {
            var claims = new List<Claim>();
            if (!user.Name.IsNullOrWhiteSpace())
                claims.Add(new Claim(AbpClaimTypes.Name, user.Name));
            if (!user.PhoneNumber.IsNullOrWhiteSpace())
                claims.Add(new Claim(AbpClaimTypes.PhoneNumber, user.PhoneNumber));
            if (!user.Email.IsNullOrWhiteSpace())
                claims.Add(new Claim(AbpClaimTypes.Email, user.Email));
            if (user.Roles != null && user.Roles.Any())
            {
                foreach (var role in user.Roles)
                {
                    claims.Add(new Claim(AbpClaimTypes.Role, role.RoleId.ToString()));
                }
                if (user.UserName == "admin")
                {
                    claims.Add(new Claim("data-permission", "3"));
                }
                else
                {
                    claims.Add(new Claim("data-permission", user.GetProperty("DataPermissionLevel", 1).ToString()));
                }
            }
            if (user.OrganizationUnits != null && user.OrganizationUnits.Any())
            {
                var orgIds = user.OrganizationUnits.Select(r => r.OrganizationUnitId);
                var organizations = (await (await OrganizationUnitRepository.AsNoTrackingAsync()).Where(r => orgIds.Contains(r.Id)).Select(r => new { r.Id, r.Code }).ToListAsync()).ToDictionary(r => r.Id, r => r.Code);
                foreach (var organizationUnit in user.OrganizationUnits)
                {
                    claims.Add(new Claim("organization", organizationUnit.OrganizationUnitId.ToString()));
                    if (organizations.ContainsKey(organizationUnit.OrganizationUnitId))
                    {
                        claims.Add(new Claim("organizationcode", organizations[organizationUnit.OrganizationUnitId]));
                    }
                }
            }
            if (user.TenantId.HasValue)
            {
                claims.Add(new Claim(AbpClaimTypes.TenantId, user.TenantId.Value.ToString()));
            }

            await SignInManager.SignInWithClaimsAsync(user, parameter.IsRememberMe, claims);

            return true;
        }
        else if (result.IsLockedOut)
        {
            throw new UserFriendlyException("账号已被锁定");
        }
        else if (result.IsNotAllowed)
        {
            throw new UserFriendlyException("用户不允许登录");
        }
        else if (!result.RequiresTwoFactor)
        {
            throw new UserFriendlyException("账号或密码错误");
        }
        return false;
    }

    public async Task LogoutAsync()
    {
        await SignInManager.SignOutAsync();
    }
}
